package com.yymx.toback.common.util;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.concurrent.Callable;

//解密Task
public class PcDecryptTask implements Callable<byte[]>{
    
    private final static Logger logger = LoggerFactory.getLogger(PcDecryptTask.class);
    
    private static final String ALGORITHOM = "RSA";
    
    
    /**
     * 最大文件解密块
     */
    private static final int PC_MAX_DECRYPT_BLOCK = 128;
    
    /**
     * 最大文件解密块
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    
    private static final ThreadLocal<Cipher> localCipher = new ThreadLocal<Cipher>() {
        @Override
        protected Cipher initialValue() {
            try {
                Security.addProvider(new BouncyCastleProvider());
                Cipher cipher = Cipher.getInstance(ALGORITHOM);
                return cipher;
            } catch (Exception e) {
                // ugly but necessary
                throw new RuntimeException(e);
            }
        }
    };
    
    private String key;
    private byte[] encryptedData;
    
    public PcDecryptTask(String key, byte[] encryptedData) {
        super();
        this.key = key;
        this.encryptedData = encryptedData;
    }
    public String getKey() {
        return key;
    }
    
    public void setKey(String key) {
        this.key = key;
    }
    
    @Override
    public byte[] call() throws Exception {
        //加载证书进行解密
        Cipher cipher = localCipher.get();
        logger.debug("DecryptTask cipher:{}",cipher);
        logger.debug("DecryptTask get getPrivateKey start");
        PrivateKey privateKey = getPrivateKey(key);
        logger.debug("DecryptTask get getPrivateKey end");
        logger.debug("DecryptTask get decryptData start");
        byte[] decryptData = decrypt(cipher,privateKey,encryptedData);
        logger.debug("DecryptTask get decryptData end");
        return decryptData;
    }
    
    public static PrivateKey getPrivateKey(String privateKey) throws Exception{
        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64Utils.decode(privateKey));
        KeyFactory keyf = KeyFactory.getInstance(ALGORITHOM);
        PrivateKey priKey = keyf.generatePrivate(priPKCS8);
        return priKey;
    }
    
    private byte[] decrypt(Cipher cipher,PrivateKey privateKey,byte[] encryptedData) throws Exception{
        // 解密byte数组最大长度限制: 128s
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }
    
    
    /**
     * 私钥解密
     * 
     * @param encryptedData
     *            已加密数据
     * @param privateKey
     *            私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData,
            String privateKey) throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHOM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(ALGORITHOM);
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > PC_MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet,
                        PC_MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet,
                        inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * PC_MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }
    
}
